Basics of Templating in PHP
One of the most annoying things when coding PHP (or any similar scripting language) is the never ending mix-up of HTML - which means design - and PHP - which means logic. That's contradictory to the idea of separating content and design as known from the so called content management systems (CMS).
I'm sure you know that a "template system" or in short "templating" could be a solution for this. But most of the usual systems are to complicated. You want to have full control of your templating? You want to know how it works? You want to do it by your own? Well, let's see what we can do.
Design: The file template.html
Let's start with the template itself. It consists of plain HTML and may look like the following one.
<html>
<body>
<h1>My Homepage</h1>
<div>
{content}
</div>
<div>
Modified last: {modified}
</div>
</body>
</html>
You may create and edit this template.html file with any HTML editor you like. Don't insert any code or content here because this file represents the design only. We will add both logic and content later in - and that's why we do all this - separate files.
Notice the template tags enclosed in round brackets. The brackets don't have a special meaning. Some templating systems use <!--content-->, {%content%} or <tpl:content /> for example. This doesn't matter as long as the syntax is unique and can't be mixed up with other parts of the HTML file. I decided to use {content} because it's very short and good to be seen when editing the template file.
Logic: The file index.php
Any PHP file starts with <?php and ends with ?>. If I say any, I mean any. That's one of the most important rules when working with PHP in my opinion. If you don't start your scripts with <?php or you are using multiple PHP tags, you are mixing HTML and PHP - and that means mixing content, design and logic. Of course it's allowed to use echo (or print()) in your scripts. You have to output some HTML in your scripts so you have to use echo. To make this clear: We don't want to create the final solution for separating content and design. All we want to do is to explore a nice, useful way to make life easier and to increase our knowledge of HTML and PHP.
The idea behind this tiny templating is a central index.php file. No matter which page the user requested, everything is done through our index.php file. The user always calls this file. The page is specified with a parameter, e.g. index.php?page=example or something like that. To make this look shorter and more professional we don't specify the filename index.php and write ./?page=example instead. The effect is the same because the Apache web server knows (in most cases) which file we want to call.
Our tiny index.php file consists of three parts. In the first part some preparations are done.
<?php
if (isset($_REQUEST['page']))
{
$page = basename($_REQUEST['page']);
}
else
{
$page = "news";
}
...
After this the variable $page contains a page name. If the user requested a particularly page and $_REQUEST['page'] contains something, its content is used. Otherwise the page "news" will be displayed by default.
In the second part of the index.php file the template is loaded and prepared.
...
$template = implode("", file("template.html"));
$modifiedTimestamp = filemtime($page . ".php");
$modified = date("Y-m-d", $modifiedTimestamp);
$template = str_replace("{modified}", $modified, $template);
$templateArray = explode("{content}", $template, 2);
...
Instead of the first line it's also possible to use $template = file_get_contents("template.html"); if your version of PHP is 4.3.0 or above. Both methods read the whole template.html file into a single string variable.
The next three lines are a simple example what template tags can be used for. Here we want to display the date when a page was modified last on any page. The first line determines the modification date and time of the page requested. The second line uses date() to convert the time stamp (seconds since January 1st, 1970) to a readable string like "2003-12-31". The third line replaces the template tag {modified} with this string.
In the last line of this part the remaining template is split into two pieces using the template tag {content} as separator. The resulting array $templateArray contains two elements after this: a header part (which is $templateArray[0]) and a footer part ($templateArray[1]).
What we want to do now is to output the header part, the page content and the footer part. This is done in the last part of the index.php file.
...
echo $templateArray[0];
include($page . ".php");
echo $templateArray[1];
?>
As you see we are using include() to include the page content here. This is also the reason why we split the template into the footer and the header part. The include() statement don't return a string so we can't handle this the same way as we did for {modified}. (It may be possible to do this by using ob_start(), ob_get_contents() and ob_end_clean(), but that's another story.)
Content: The other files
The content files have to be named the same way as we call them using the page=... parameter with the exception of the file extension. This always has to be .php, so when we call ./?page=example the file example.php will be included.
The files may contain plain HTML code and may look like this:
<h2>Example</h2>
<p>
This is an example page.
</p>
Please note that these files don't contain any header or footer data. They just contain the content to be included at the position declared by {content} in the template.html file.
Because our content files are usual PHP files they may contain PHP code too.
<?php
echo "I'm a content file too.";
?>
This way you are able to create web pages (almost) without any knowledge of the design or the menu. You are able to change the menu as fast as you did it using a frameset before. Even more: You are able to change the colors of all pages on a single point. Use CSS for this, for example put h2 { background-color:#bbcc99; } in the template file. This sets a bright yellow background to all <h2> headlines. Don't use any specific colors in the content files if possible. Use a central CSS part instead.
A last hint: If that's to simple for you and you want to use something more eleet, take a look at PHP's XSLT functions.